home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / listings / v_13_02 / cepek / fmutl.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-25  |  24.1 KB  |  713 lines

  1. /* fmutl.c - flash memory utility routines */
  2.  
  3. /***
  4. Created 1993-4.  Copyright (C)
  5. Management Graphics, Inc., Minneapolis, Minnesota
  6. All Rights Reserved.
  7.  
  8. Confidential information - For limited distribution
  9. to authorized persons within MGI only.  This software
  10. and data are protected as an unpublished work under
  11. the U.S. Copyright Act of 1976.
  12.  
  13.  
  14. --------------  Module Description:  --------------
  15.  
  16. This module contains utility routines which interface to the
  17. Flash Memory devices.  These replace EPROMs for firmware.
  18.  
  19. The primary external routines are:
  20.     fm_status() - resets chips and returns FMINFO structure data;
  21.     fm_write() - writes data to the flash memory.
  22. Data in flash is read like normal memory.
  23.  
  24. IMPORTANT:  The E_ITER and W_ITER constants are implementation-dependent.
  25. They control time-out check loops, and must be maintained.  Re-measure the
  26. correct values for these constants if any of the following change:
  27.     - the code in the loops controlled by those constants;
  28.     - the compiler or optimization switches;
  29.     - CPU clock speed;
  30.     - memory access time;
  31.     - anything else which may change the execution speed
  32.       of the instructions in those loops.
  33. Note that these constants set worst-case limits, so things like
  34. interrupt handlers can be ignored, since they will only minimally
  35. lengthen the time-out checks.
  36.  
  37.  
  38.     GENERAL NOTES:
  39.  
  40. This was originally coded for AMD Am29F010 and Am29F040 devices.
  41. See also the Advanced Micro Devices (AMD) spec sheets (AMD publication
  42. numbers 16736 and 17113, respectively).
  43.  
  44. Note that our hardware arranges that word accesses are handled as two
  45. parallel byte accesses to the devices (simultaneous odd- and even-byte
  46. device accessing).  This is why:
  47.     - all read and write accesses to Flash Memory
  48.       are made as 16-bit words (unsigned shorts);
  49.     - command codes are word wide (repeating
  50.       the command bytes twice); and why
  51.     - some length and address values are shifted
  52.       by one (byte- to word-address conversion).
  53. This has significant impact for some algorithms; see the Timeout
  54. Detections Notes, below.
  55.  
  56.  
  57.     TIMING NOTES:
  58.  
  59. Timing tests on 2-Mar-94 with not-quite-new chips indicate the following
  60. (random words erased then random words written):
  61.              Erase  Write          Bytes  Bits   Erase   Write
  62.    Device    all    all    Total   (per chip)    sector  sector  Total
  63.    ========  -----  -----  -----   -----  ----   ------  ------  -----
  64.    Am29F010   4.2    4.9    9.1    128K    1M     0.9     0.8     1.7
  65.    Am29F040   8.7   14.1   22.8    512K    4M     2.2     1.9     4.1
  66.  
  67. The spec sheets imply that erase and write times may significantly
  68. increase with device age/use.  Erases may take x10 to x20 (or more)
  69. as long; writing may take x60 to x4000 (yes, 4K) as long.
  70.  
  71. It was observed that erasing 1's takes longer (up to x4)
  72. than erasing 0's (i.e. erasing previously written values).
  73.  
  74. It was also observed that writing 0's takes longer (up to x3)
  75. than writing 1's (presumably because erase leaves all 1's).
  76.  
  77. 7-Apr-94: the fm_cpy() routine was made significantly (>11%) faster; the
  78. table above does *not* reflect this improvement.
  79.  
  80.  
  81.  
  82.     CODING NOTES AND WARNINGS:
  83.  
  84. It is assumed that the base address of the Flash Memory is an even
  85. multiple of the total size of the Flash memory.  In other words, the
  86. address of the first location in Flash is assumed to have all possible
  87. lower bits zero.  This allows the code to mask off the lower bits to
  88. obtain the Flash base address.  It is also assumed that the number of
  89. addressable bytes are a power of 2.
  90.  
  91. Note also that address 0x00000000 can be a valid address.  It doesn't
  92. make sense in 'C', but it makes sense in hardware.  Looping structures
  93. like:  for (ptr1 = ptr0;  ptr1 >= ptr2;  ++ptr1) {...}
  94. tend to be dangerous when ptr2 is allowed to be zero.  Safe programming
  95. will also allow for pointers high in memory (e.g: 0xFFFFFFFE).  These
  96. considerations require a more defensive than usual approach in the
  97. code.  Examples of safer approaches:  loops not based on pointer
  98. comparisons, and normalizing pointers before looping.
  99.  
  100.  
  101.     NOTES ABOUT TIMEOUT DETECTION:
  102.  
  103. A timeout happens when an erase or byte-write operation takes "too long"
  104. (see the Timing Notes section above).  The chips have built-in timers to
  105. detect this, but the code to take advantage of this feature ends up
  106. being a little cryptic.  Here is a brief description.
  107.  
  108. When a chip times out, bit 5 (DQ5) is raised in the value read back.
  109. It sounds simple.  However, this condition is only valid when DQ7 is
  110. in the NOT-DATA-POLLING state (the inverse of the expected value).
  111. I.e, DQ5 can only be trusted if DQ7 does not yet indicate completion of
  112. the erase or write.  Additionally, since two chips service each word
  113. (odd & even byte), each byte needs to be checked independently.  This
  114. is because one chip may timeout while the other is ok.  To further
  115. complicate things, DQ5 and DQ7 may change simultaneously; this requires
  116. a second check to confirm that the timeout value read isn't actually
  117. the chip data lines in transition to valid data.
  118.  
  119. The timeout detection code here first constructs a to_val temporary,
  120. which, for each byte, has DQ5 set, has DQ7 in the NOT-DATA-POLLING state,
  121. and the other 6 bits are zero.  To test for timeout, the DQ5 and DQ7 bits
  122. are first masked out of the current value, and then xor-ed with to_val.  If
  123. the result for the odd *or* even byte is zero, then a timeout has occurred.
  124.  
  125. For speed, the code only checks for timeout every so often.  This allows
  126. the inner-loop to be as fast as possible, which is very important to the
  127. fm_cpy() routine since it writes each byte this way.  See also the data
  128. sheet for an explanation of the recommended "!Data Polling Algorithm"
  129. (which this is based on).
  130.  
  131. When interpreting the timeout error codes, keep in mind DQ6, the
  132. Toggle Bit (which is not used in the algorithms here).
  133.  
  134. ***/
  135.  
  136.  
  137. #include <string.h>
  138. #include "fmutl.h"
  139.  
  140.  
  141.     /** all these magic numbers are from the AMD spec sheets: **/
  142.  
  143.     /* address and data for command writes: */
  144. #define    FM_ADDR1    0x5555        /* these are for the first "magic */
  145. #define    FM_DATA1    0xAAAA        /*   write" of a command preamble */
  146. #define    FM_ADDR2    0x2AAA        /* these are for the second "magic */
  147. #define    FM_DATA2    0x5555        /*    write" of a command preamble */
  148.  
  149.     /* command codes: */
  150. #define    FMCMD_RESET    0xF0F0        /* "Read/Reset Command" */
  151. #define    FMCMD_AUTOSEL    0x9090        /* "Autoselect Command" */
  152. #define    FMCMD_WRITE    0xA0A0        /* "Byte Program Command" */
  153. #define    FMCMD_ERASE    0x8080        /* "Chip or Sector Erase Command" */
  154.  
  155.     /* use this to skip last step of a command preamble [see fm_cmd()]: */
  156. #define    FMCMD_NONE    0x0000        /* no command */
  157.  
  158.     /* secondary command codes for erase commands: */
  159. #define    FMCMD2_SECTOR    0x3030        /* "Sector Erase" */
  160. #define    FMCMD2_CHIP    0x1010        /* "Chip Erase" */
  161.  
  162.     /* miscellaneous other flash memory constants: */
  163. #define    FM_PROT_SECT_MASK  0x0101    /* protected sector indicator bit(s) */
  164. #define    FM_ERASE_VAL    0xFFFF        /* value of an erased location */
  165. #define    FM_TIMEOUT    0x2020        /* DQ5 - goes hi if device times out */
  166. #define    FM_NDATA_POLL    0x8080        /* DQ7 - inverse of final data */
  167.  
  168.     /* special address offsets for Autoselect command reads: */
  169. #define    FM_AO_MANUF    0    /* for manufacturer code readback */
  170. #define    FM_AO_DEVID    1    /* for device ID readback */
  171. #define    FM_AO_PROT_SECT    2    /* for protected sector readbacks */
  172.  
  173.  
  174. FMERR fm_err;        /* global Flash error structure */
  175.  
  176. static char *ErrMsgs[] = {    /** fm_err.code and fm_err.pMsg values: **/
  177.     "x?? Unknown err",    /* 0: unused */
  178.     "Bad devID codes",    /* 1: unknown/mismatched/missing/bad chips */
  179.     "Erase fail-safe",    /* 2: sector erase fail-safe triggered */
  180.     "Erase timed out",    /* 3: sector erase command timed out */
  181.     "Write fail-safe",    /* 4: byte write fail-safe triggered */
  182.     "Write timed out",    /* 5: byte write programming timed out */
  183.     "fm_write params",    /* 6: address or length parameter was odd */
  184.     "fm_write overfl",    /* 7: attempt to write past end of flash */
  185.     };    /** see also ErrMsg[] in fm_error() **/
  186.  
  187.  
  188. static FMINFO DevTable[] = {    /* known device data table: */
  189.      /*  MfrID   DevID  EFS WFS NS SectSiz*2  TotSize*2  NP FP DevNameString  */
  190.     0x0101, 0x2020, 20, 90, 8, 0x04000*2, 0x20000*2, 0, 0, "AMD Am29F010",
  191.     0x0101, 0xA4A4, 40, 90, 8, 0x10000*2, 0x80000*2, 0, 0, "AMD Am29F040",
  192.     };
  193.  
  194.  
  195.     /* the routines in this module need to share this data: */
  196. static unsigned volatile short *pBase;    /* ptr to the base address of Flash */
  197. static FMINFO FMInfo;            /* info specific to these chips */
  198.  
  199.  
  200.     /* make it easy to give flashtst.c access to some internal routines: */
  201. #ifndef STATIC
  202. #define STATIC static
  203. #endif
  204.  
  205. #define    DUMP_FE    0    /* non-zero to dump function params on entry */
  206.  
  207.  
  208.  
  209. /*****************************************************************************
  210. Set values in the global error structure.
  211. *****************************************************************************/
  212.  
  213. void fm_error(addr, exp, act, errcode)
  214. unsigned short *addr, exp, act;
  215. int errcode;        /* only the low byte is significant */
  216. {
  217. static    char ErrMsg[20];
  218. static    char Hex[] = "0123456789ABCDEF";
  219.  
  220.     /* fill in the easy stuff: */
  221.     fm_err.addr = addr;
  222.     fm_err.exp = exp;
  223.     fm_err.act = act;
  224.     fm_err.code = (unsigned char) errcode;
  225.  
  226.     /* either use a built-in message, or construct one using errcode: */
  227.     if (1 <= errcode  &&  errcode < ARRAY_LEN(ErrMsgs))
  228.         fm_err.pMsg = ErrMsgs[errcode];
  229.     else
  230.     {    strncpy(ErrMsg, ErrMsgs[0], sizeof(ErrMsg));
  231.         ErrMsg[1] = Hex[fm_err.code / 16];
  232.         ErrMsg[2] = Hex[fm_err.code % 16];
  233.         fm_err.pMsg = ErrMsg;
  234.     }
  235. }
  236.  
  237.  
  238.  
  239. /*****************************************************************************
  240. Send unlock commands to the chips.  This magic permutation of writes takes
  241. the chips out of normal read-only mode, and puts them into the indicated
  242. command mode (see the FMCMD_ constants).  This code is replicated in the
  243. fm_cpy() routine for performance reasons.
  244. *****************************************************************************/
  245.  
  246. static void fm_cmd(cmd)
  247. unsigned short cmd;        /* command code = an FMCMD_ constant */
  248. {
  249. /*?*  printf("fm_cmd(%04x), pBase = %08x\n", cmd, pBase); /**/
  250.  
  251.     /* write first magic value to first magic location: */
  252.     *(pBase + FM_ADDR1) = FM_DATA1;
  253.  
  254.     /* write second magic value to second magic location: */
  255.     *(pBase + FM_ADDR2) = FM_DATA2;
  256.  
  257.     /* if desired, write a command code to the final magic location: */
  258.     if (cmd != FMCMD_NONE)
  259.         *(pBase + FM_ADDR1) = cmd;
  260. }
  261.  
  262.  
  263.  
  264. /*****************************************************************************
  265. Reset the devices.  Two resets are performed because the first may not
  266. be accepted if the devices were left in a strange state (e.g. if a prior
  267. command sequence was interrupted before completion).  The second is sure
  268. to be heard by sane devices.
  269. *****************************************************************************/
  270.  
  271. static void fm_reset()
  272. {
  273.     fm_cmd(FMCMD_RESET);
  274.     fm_cmd(FMCMD_RESET);
  275. }
  276.  
  277.  
  278.  
  279. /*****************************************************************************
  280. Provides information about protected sectors in the chips.  FMInfo and pBase
  281. are assumed to be initialized.  Leaves the chips in AutoSelect mode.
  282. *****************************************************************************/ 
  283.  
  284. static void fm_protected(pFirstProt, pNumProt)
  285. int *pFirstProt, *pNumProt;    /* see the FMINFO struct for details */
  286. {
  287.     unsigned volatile short *ptr;    /* ptr into flash space */
  288.     unsigned short val;        /* value read back */
  289.     int sect;            /* iterates thru the sectors */
  290.  
  291.     /* init our pointer to the magic offset in the last sector: */
  292.     ptr = pBase + ((FMInfo.TotalSize >> 1) + FM_AO_PROT_SECT);
  293.     ptr -= FMInfo.SectorSize >> 1;
  294.  
  295.     /* command the chip to read back the protected sector data: */
  296.     fm_cmd(FMCMD_AUTOSEL);
  297.  
  298.     /* initialize these to mean "no protected sectors found": */
  299.     *pNumProt = 0;
  300.     *pFirstProt = -1;
  301.  
  302.     /* loop checking each sector: */
  303.     for (sect = FMInfo.NumSect_;  --sect >= 0;  )
  304.     {
  305.         /* bit set indicates sector is protected: */
  306.         val = *ptr & FM_PROT_SECT_MASK; 
  307.         if (val)
  308.         {    /* if both devices don't agree, error out: */
  309.             if (val != FM_PROT_SECT_MASK)
  310.             {    *pNumProt = -2;
  311.                 return;
  312.             }
  313.  
  314.             /* if protection is non-continuous, error out: */
  315.             if (*pFirstProt != -1  &&  *pFirstProt != sect + 1)
  316.             {    *pNumProt = -1;
  317.                 return;
  318.             }
  319.  
  320.             /* set index to this sector and bump the count: */
  321.             *pFirstProt = sect;
  322.             ++*pNumProt;
  323.         }
  324.  
  325.         /* bump pointer to the magic location in the next sector: */
  326.         ptr -= FMInfo.SectorSize >> 1;
  327.     }
  328. }
  329.  
  330.  
  331.  
  332. /*****************************************************************************
  333. Resets and returns data about the flash chips.  Fills in our local
  334. FMInfo structure, and optionally fills in an FMINFO structure for the
  335. caller.  Leaves the chip(s) in the normal ROM state.  Zero is returned
  336. on error and fm_err contains failure data. 
  337. *****************************************************************************/
  338.  
  339. fm_status(pbase, pFMInfo)
  340. unsigned short *pbase;    /* must point to the *base* of the Flash memory */
  341. FMINFO *pFMInfo;    /* may be NULL */
  342. {
  343.     unsigned short mfr, dev;    /* IDs read back from devices */
  344.     FMINFO *pDev;            /* traverses the DevTable[] */
  345.  
  346.     /* share this with other routines in this module: */
  347.     pBase = pbase;
  348.  
  349.     /* clear out error structure: */
  350.     memset(&fm_err, 0, sizeof(fm_err));
  351.  
  352.     /* reset the chip: */
  353.     fm_reset();
  354.  
  355.     /* send down command to read back the embedded data: */
  356.     fm_cmd(FMCMD_AUTOSEL);
  357.  
  358.     /* read back the codes: */
  359.     mfr = *(pBase + FM_AO_MANUF);
  360.     dev = *(pBase + FM_AO_DEVID);
  361.  
  362.     /* check for known devices: */
  363.     pDev = DevTable + ARRAY_LEN(DevTable);
  364.     while (--pDev >= DevTable)
  365.         if (pDev->MfrID_ == mfr  &&  pDev->DevID_ == dev)
  366.         {    memcpy(&FMInfo, pDev, sizeof(FMInfo));
  367.             break;
  368.         }
  369.  
  370.     /* if not found in our table, return error: */
  371.     if (pDev < DevTable)
  372.     {    /* (could also be mismatched, missing, or fried devices) */
  373.         fm_reset();
  374.         fm_error(pBase, mfr, dev, 1);
  375.         return 0;
  376.     }
  377.  
  378.     /* go get & fill in protected sector information: */
  379.     fm_protected(&FMInfo.FirstProt, &FMInfo.NumProt);
  380.  
  381.     /* fill in structure for caller if they want us to: */
  382.     if (pFMInfo)
  383.         memcpy(pFMInfo, &FMInfo, sizeof(FMINFO));
  384.  
  385.     /* exit readback mode and we're done: */
  386.     fm_reset();
  387.     return 1;
  388. }
  389.  
  390.  
  391.  
  392. /*****************************************************************************
  393. Handle a time-out or fail-safe error.  The expected value is checked with
  394. the actual value to determine if a time-out happened.  fm_error is called
  395. with errcode for a fail-safe error, or with errcode+1 for a time-out error.
  396. *****************************************************************************/
  397.  
  398. static void fm_tofs_error(addr, exp, act, errcode)
  399. unsigned short *addr, exp, act;
  400. int errcode;        /* fail-safe error=errcode; time-out error=errcode+1 */
  401. {
  402.     unsigned short to_val;        /* value used to check for time-out */
  403.     unsigned short tmp;        /* time-out check temporary */
  404.  
  405.     /* this value has timeout & data-poll bits set to mean time-out: */
  406.     to_val = (~exp & FM_NDATA_POLL) | FM_TIMEOUT;
  407.  
  408.     /* mask out the interesting bits and xor them: */
  409.     tmp = (act & (FM_TIMEOUT | FM_NDATA_POLL)) ^ to_val;
  410.  
  411.     /* both bits clear in a byte means that chip timed-out: */
  412.     if (!(tmp & 0x00ff)  ||  !(tmp & 0xff00))
  413.         ++errcode;
  414.  
  415.     /* timeout error or fail-safe error handling: */
  416.     fm_error(addr, exp, act, errcode);
  417.  
  418.     /* leave chips usable */
  419.     fm_reset();
  420. }
  421.  
  422.  
  423.  
  424. /*****************************************************************************
  425. Erase by sectors.  The sector(s) which are overlapped by the indicated
  426. flash address memory range are erased.  Returns non-zero on success;
  427. otherwise zero is returned and fm_err contains failure data.  Assumes
  428. FMInfo and pBase are initialized.
  429. *****************************************************************************/
  430.  
  431. #define    E_FS_ITER  240000     /* number of fail-safe loop iterations per sec. */
  432.     /** Re-measure this constant if the fail-safe loop is changed! **/
  433.  
  434. STATIC fm_sector_erase(pLow, pHigh)
  435. volatile unsigned short *pLow;    /* ptr somewhere into first sector to erase */
  436. volatile unsigned short *pHigh;    /* ptr somewhere into last sector to erase */
  437. {
  438.     size_t SectorAddrMask;     /* converts ptr to offset within a sector */
  439.     size_t SectOffset;        /* offset into ending sector */
  440.     unsigned long fail_safe;    /* safety counter */
  441.  
  442. #if DUMP_FE
  443. /*?*/ printf("fm_sector_erase(%08x %08x)\n", pLow, pHigh);
  444. #endif
  445.     /* this masks off address bits indicating offsets into a sector: */
  446.     SectorAddrMask = FMInfo.SectorSize - 1;
  447.  
  448.     /* set end pointer to the last location in it's sector: */
  449.     SectOffset = ((size_t) pHigh) & SectorAddrMask;
  450.     pHigh = pHigh - (SectOffset >> 1) + (FMInfo.SectorSize >> 1) - 1;
  451.  
  452.     /* send erase preamble sequence: */
  453.     fm_cmd(FMCMD_ERASE);
  454.  
  455.     /* send sector erase preamble sequence: */
  456.     fm_cmd(FMCMD_NONE);
  457.  
  458.     /* loop indicating which sectors to erase: */
  459.     while (pLow <= pHigh)
  460.     {
  461.         /* tell chip to erase this sector: */
  462.         *pLow = FMCMD2_SECTOR;
  463.  
  464.         /* bump pointer by one sector: */
  465.         pLow += FMInfo.SectorSize >> 1;
  466.     }
  467. #if 00
  468. /*?*/ DspWrite("ready\n ");  DspNum(FMInfo.MaxErase_, 1, 0, 8);
  469. /*?*/ pHigh = 100;  Delay(2000);
  470. /*?*/ DspWrite("go!\n ");  VBeep(50);
  471. #endif
  472.     /* loop checking for erase completion: */
  473.     for (fail_safe = FMInfo.MaxErase_ * E_FS_ITER;  --fail_safe;  )
  474.         if (*pHigh == FM_ERASE_VAL)
  475.             return 1;  /* done! */
  476. #if 00
  477. /*?*/  DspWrite("done\n "); VBeep(200);  while (1) { }  /**/
  478. #endif
  479.     /* time-out and fail-safe error handling: */
  480.     fm_tofs_error(pHigh, FM_ERASE_VAL, *pHigh, 2);
  481.     return 0;
  482. }
  483.  
  484.  
  485.  
  486. /*****************************************************************************
  487. Internal routine to copy data to Flash Memory.  It is assumed that the
  488. destination area has been erased, and that bytes is an even number.
  489. Returns non-zero on success; zero is returned on error and fm_err contains
  490. failure data.  FMInfo and pBase are assumed initialized.
  491.   * * * NOTE: This routine has been hand optimized for performance. * * *
  492. *****************************************************************************/
  493.  
  494. #define    W_FS_ITER  350    /* number of fail-safe loop iterations per msec */
  495.     /** Re-measure this constant if the fail-safe loop is changed! **/
  496.  
  497. static fm_cpy(pDst, pSrc, bytes)
  498. register volatile unsigned short *pDst;    /* where in Flash to copy data to */
  499. register unsigned short *pSrc;        /* where to copy data from */
  500. register size_t bytes;            /* # bytes to copy -- must be even */
  501. {
  502.     register volatile unsigned short *ptr1;    /* fast FM_ADDR1 ptr */
  503.     register volatile unsigned short *ptr2;    /* fast FM_ADDR2 ptr */
  504.     register unsigned short fmd1, fmd2;    /* fast FM_DATA values */
  505.     register unsigned short val;        /* value read in verify loop */
  506.     register unsigned short fail_safe;    /* safety counter */
  507.  
  508. #if DUMP_FE
  509. /*?*/    printf("fm_cpy(%08x %08x %06x)\n", pDst, pSrc, bytes);
  510. #endif
  511.     /* put these in registers for speed: */
  512.     ptr1 = pBase + FM_ADDR1;
  513.     ptr2 = pBase + FM_ADDR2;
  514.     fmd1 = FM_DATA1;
  515.     fmd2 = FM_DATA2;
  516.  
  517.     /* loop writing and verifying a word at a time: */
  518.     while (bytes)
  519.     {
  520.         /* this duplicates a call to fm_cmd(), for speed: */
  521.         *ptr1 = fmd1;
  522.         *ptr2 = fmd2;
  523.         *ptr1 = FMCMD_WRITE;
  524.  
  525.         /* write two bytes: */
  526.         *pDst = *pSrc;
  527.  
  528.         /* do this here - a bit of parallel processing: */
  529.         bytes -= 2;
  530.         val = *pSrc;
  531. #if 000
  532. /*?>^*/    register unsigned short j;
  533. /*?*/ DspWrite("READY\n ");  DspNum(FMInfo.MaxWrite_, 1, 0, 8);
  534. /*?*/ pDst = 100;  Delay(2000);
  535. /*?*/ DspWrite("GO!\n ");  VBeep(50);
  536. /*?*/ for (j = 1000;  --j;  )
  537. #endif
  538.         /* loop checking for write completion: */
  539.         for (fail_safe = FMInfo.MaxWrite_ * W_FS_ITER;  --fail_safe;  )
  540.             if (*pDst == val)
  541.                 break;  /* done! */
  542. #if 000
  543. /*?*/  DspWrite("DONE\n "); VBeep(200);  while (1) { }  /**/
  544. #endif
  545.         /* if loop was exhausted, go handle the error: */
  546.         if (fail_safe == 0)
  547.         {    fm_tofs_error(pDst, val, *pDst, 4);
  548.             return 0;
  549.         }
  550.  
  551.         /* move to the next location and loop back: */
  552.         ++pSrc;
  553.         ++pDst;
  554.     }
  555.  
  556.     /* return happy: */
  557.     return 1;
  558. }
  559.  
  560.  
  561.  
  562. /*****************************************************************************
  563. Write data to the Flash Memory.  Returns non-zero on success; on error
  564. zero is returned and fm_err contains failure data.  The pDst and length
  565. parameters should be word aligned.
  566.  
  567. The pScrBuf parameter determines the fate of words erased in a sector but
  568. not written with new data:
  569.     if (pScrBuf == NULL), then writes to partial sectors leave the
  570.         unwritten words of those sectors erased & unwritten.
  571.         if (pScrBuf != NULL), then *pScrBuf is assumed to point to at least 
  572.                 FMINFO.SectorSize bytes, and unwritten (but erased) words
  573.                 in partially written sectors will be saved (in the scratch
  574.                 buffer) and re-written, maintaining their prior values.
  575. *****************************************************************************/
  576.  
  577. fm_write(pbase, pDst, pSrc, length, pScrBuf)
  578. unsigned short *pbase;        /* ptr to the base of the Flash memory */
  579. unsigned short *pDst;        /* where to write in Flash Memory space */
  580. unsigned short *pSrc;        /* bytes from here are copied to Flash */
  581. size_t length;            /* copy this many bytes from pSrc to pDst */
  582. unsigned short *pScrBuf;     /* scratch buffer for partial sector writes */
  583. {
  584.     size_t wri_len;         /* # partial sector bytes of user data */
  585.     size_t keep_before;     /* # partial sector bytes prior to user data */
  586.     size_t keep_after;     /* # partial sector bytes after user data */
  587.     size_t offset;         /* word offsets for various uses */
  588.     size_t SectorAddrMask;     /* converts ptr to offset within a sector */
  589.     unsigned short *pSector; /* ptr to even sector boundary */
  590.     char errcode;         /* non-zero indicates a parameter error */
  591.  
  592. #if DUMP_FE
  593.     printf("fm_write(%08x %08x %08x %08x %08x)\n", pbase, pDst, pSrc, length, pScrBuf);
  594. #endif
  595.     /* share this with other routines in this module: */
  596.     pBase = pbase;
  597.  
  598.     /* clear out error structure: */
  599.     memset(&fm_err, 0, sizeof(fm_err));
  600.  
  601.     /* reset the chip and load FMInfo with info about it: */
  602.     if (!fm_status(pBase, (FMINFO *) 0))
  603.         return 0;
  604.  
  605.     /* check input parameters for word alignment and overflow: */
  606.     errcode = 0;
  607.     if (((size_t) pDst & 1) || (length & 1))
  608.         errcode = 6;
  609.     else if (pDst + (length >> 1) > pBase + (FMInfo.TotalSize >> 1))
  610.         errcode = 7;
  611.  
  612.     /* these errors return length in pieces in the exp and act fields: */
  613.     if (errcode)
  614.     {    fm_error(pDst, (unsigned short) length >> 8,
  615.                    (unsigned short) length & 0xFFFF, errcode);
  616.         return 0;
  617.     }
  618.  
  619.     /* this masks off address bits indicating offsets into a sector: */
  620.     SectorAddrMask = FMInfo.SectorSize - 1;
  621.  
  622.     /* see if we are starting on an even sector boundary: */
  623.     keep_before = ((size_t) pDst) & SectorAddrMask;
  624.  
  625.     /* if the first sector is a partial, do it separately: */
  626.     if (keep_before)
  627.     {
  628.         /* get a pointer to the start of the sector: */
  629.         pSector = pDst - (keep_before >> 1);
  630.  
  631.         /* compute # bytes in this sector being written by user: */
  632.         wri_len = FMInfo.SectorSize - keep_before;
  633.         if (length < wri_len)
  634.             wri_len = length;
  635.  
  636.         /* compute # bytes to keep after area being written: */
  637.         keep_after = FMInfo.SectorSize - keep_before - wri_len;
  638.  
  639.         /* read existing Flash sector into scratch buffer: */
  640.         if (pScrBuf)
  641.             memcpy(pScrBuf, pSector, FMInfo.SectorSize);
  642.  
  643.         /* erase the sector: */
  644.         if (!fm_sector_erase(pDst, pDst))
  645.             return 0;
  646.  
  647.         /* copy the user's data to Flash: */
  648.         if (!fm_cpy(pDst, pSrc, wri_len))
  649.             return 0;
  650.  
  651.         /* restore data in sector before user's data: */
  652.         if (pScrBuf)
  653.             if (!fm_cpy(pSector, pScrBuf, keep_before))
  654.                 return 0;
  655.  
  656.         /* restore data in sector after user's data: */
  657.         if (pScrBuf  &&  keep_after)
  658.         {    offset = (wri_len + keep_before) >> 1;
  659.             if (!fm_cpy(pSector + offset,
  660.                     pScrBuf + offset, keep_after))
  661.                 return 0;
  662.         }
  663.  
  664.         /* bump pointers: */
  665.         pDst += (wri_len >> 1);
  666.         pSrc += (wri_len >> 1);
  667.         length -= wri_len;
  668.  
  669.         /* quit if done: */
  670.         if (!length)
  671.             return 1;
  672.     }
  673.  
  674.     /* see if we are ending on an even sector boundary: */
  675.     wri_len = length & SectorAddrMask;
  676.  
  677.     /* handle partial final sector: */
  678.     if (wri_len)
  679.     {
  680.         /* get a pointer to the start of the final sector: */
  681.         pSector = pDst + ((length - wri_len) >> 1);
  682.  
  683.         /* compute # bytes to keep after area being written: */
  684.         keep_after = FMInfo.SectorSize - wri_len;
  685.  
  686.         /* read partial existing Flash sector into scratch buffer: */
  687.         if (pScrBuf)
  688.             memcpy(pScrBuf, pSector + (wri_len >> 1), keep_after);
  689.     }
  690.  
  691. /*?* VBeep(50);  /**/
  692.     /* erase the remaining sector(s): */
  693.     if (!fm_sector_erase(pDst, pDst + (length >> 1) - 1))
  694.         return 0;
  695. /*?* VBeep(50);  Delay(200);  VBeep(50);  Delay(200);  /**/
  696.  
  697.     /* copy the user's data to Flash: */
  698.     if (!fm_cpy(pDst, pSrc, length))
  699.         return 0;
  700. /*?* VBeep(50);  Delay(200);  VBeep(50);  Delay(200);  VBeep(50);  /**/
  701.  
  702.     /* restore data in sector after user's data: */
  703.     if (wri_len  &&  pScrBuf  &&  keep_after)
  704.     {    offset = wri_len >> 1;
  705.         if (!fm_cpy(pSector + offset, pScrBuf, keep_after))
  706.             return 0;
  707.     }
  708.  
  709.     /* return happy: */
  710.     return 1;
  711. }
  712.  
  713.